using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Xml;

namespace RrslLite.Net.Attributes
{
    //[Serializable]
    public class CastleDataContractSerializerAttribute : Attribute, IOperationBehavior
    {
        private CastleDataContractSerializerOperationBehavior _innerOperationBehavior;

        public CastleDataContractSerializerAttribute()
        {
        }

        public CastleDataContractSerializerAttribute(OperationDescription operation)
        {
            _innerOperationBehavior = new CastleDataContractSerializerOperationBehavior(operation);
        }

        public CastleDataContractSerializerAttribute(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
        {
            _innerOperationBehavior = new CastleDataContractSerializerOperationBehavior(operation, dataContractFormatAttribute);
        }

        public void Validate(OperationDescription operationDescription)
        {
            if (_innerOperationBehavior == null)
            {
                _innerOperationBehavior = new CastleDataContractSerializerOperationBehavior(operationDescription);
            }

            (_innerOperationBehavior as IOperationBehavior).Validate(operationDescription);
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            ReplaceDataContractSerializerOperationBehavior(operationDescription);
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
            ReplaceDataContractSerializerOperationBehavior(operationDescription);
        }

        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
        {
            if (_innerOperationBehavior == null)
            {
                _innerOperationBehavior = new CastleDataContractSerializerOperationBehavior(operationDescription);
            }

            (_innerOperationBehavior as IOperationBehavior).AddBindingParameters(operationDescription, bindingParameters);
        }

        private void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
        {
            var operationBehavior = description.Behaviors.SingleOrDefault(predicate => predicate.GetType().Name == "DataContractSerializerOperationBehavior");

            if (operationBehavior == null)
            {
                return;
            }

            description.Behaviors.Remove(operationBehavior);
            description.Behaviors.Add(new CastleDataContractSerializerOperationBehavior(description));
        }
    }

    public class CastleDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
    {
        public CastleDataContractSerializerOperationBehavior(OperationDescription operation)
            : base(operation)
        {
        }

        public CastleDataContractSerializerOperationBehavior(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
            : base(operation, dataContractFormatAttribute)
        {
        }

        public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
        {
            return new DataContractSerializer(type, name, ns, MergeKnownTypesWithDynamicProxyTypes(knownTypes));
        }

        public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
        {
            return new DataContractSerializer(type, name, ns, MergeKnownTypesWithDynamicProxyTypes(knownTypes));
        }

        private static IEnumerable<Type> MergeKnownTypesWithDynamicProxyTypes(IEnumerable<Type> knownTypes)
        {
            var tmp = new HashSet<Type>();

            if (knownTypes != null)
                tmp.UnionWith(knownTypes);

            //tmp.UnionWith(CastleDynamicProxyAssemblies.GetTypes());

            return tmp;
        }
    }


    public class CastleDataContractSerializer : XmlObjectSerializer
    {
        public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
        {
            throw new NotImplementedException();
        }

        public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
        {
            throw new NotImplementedException();
        }

        public override void WriteEndObject(XmlDictionaryWriter writer)
        {
            throw new NotImplementedException();
        }

        public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
        {
            throw new NotImplementedException();
        }

        public override bool IsStartObject(XmlDictionaryReader reader)
        {
            throw new NotImplementedException();
        }
    }

}